package com.java.generic;

import com.google.common.collect.Lists;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @program: study
 * @className: CompilerIntelligence
 * @description: TODO
 * @author: kukuxiahuni
 * @create: 2019-12-06 17:46
 * @version: v1.0
 **/
class Fruit {
}

class Apple extends Fruit {
}

class Jonathan extends Apple {
}

class Orange extends Fruit {
}

public class CompilerIntelligence {
    static List<Apple> apples = Arrays.asList(new Apple());
    static List<Fruit> fruit = Arrays.asList(new Fruit());

    static <T> T readExact(List<T> list) {
        return list.get(0);
    }

    // A static method adapts to each call:
    static void f1() {
        Apple a = readExact(apples);
        Fruit f = readExact(fruit);
        f = readExact(apples);
    }

    static void f2() {
        Reader<Fruit> fruitReader = new Reader<>();
        Fruit f = fruitReader.readExact(fruit);
//         Fruit a = fruitReader.readExact(apples);
        // error: incompatible types: List<Apple>
        // cannot be converted to List<Fruit>
    }

    static void f3() {
        CovariantReader<Fruit> fruitReader = new CovariantReader<>();
        Fruit f = fruitReader.readCovariant(fruit);
        Fruit a = fruitReader.readCovariant(apples);
    }

    public static void main(String[] args) {
        List<? extends Fruit> fruits = Lists.newArrayList(new Apple(), new Jonathan());

        Fruit fruit = fruits.get(0);
        fruits.add(null);


        List<? super Apple> superFruits = new ArrayList<>();
        superFruits.add(new Apple());

        Apple apple = (Apple) superFruits.get(0);

        f1();
        f2();
        f3();
    }

    // A class type is established
    // when the class is instantiated:
    static class Reader<T> {
        T readExact(List<T> list) {
            return list.get(0);
        }
    }

    static class CovariantReader<T> {
        T readCovariant(List<? extends T> list) {
            return list.get(0);
        }
    }

    static class ReaderProducer<T>{
        T read(List<? extends T> list) {
            return list.get(0);
        }
    }

}